package com.ihome.framework.core.event;

import static org.junit.Assert.assertTrue;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicLong;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.ihome.framework.core.cache.redis.IHomeRedisClient;
import com.ihome.framework.core.event.command.AccountChangeCommand;
import com.ihome.framework.core.event.command.CommandEventBusSpringBeanRegister.CommandEvent;
import com.ihome.framework.core.event.command.SimpleCommandEventBusHandler;
import com.ihome.framework.core.event.command.buffer.CommandBuffer;
import com.ihome.framework.core.event.command.lua.TallyAccountLuaCommandScript;
import com.ihome.framework.core.event.command.lua.FrozenAccountLuaCommandScript;
import com.ihome.framework.core.event.command.lua.LuaCommandUploadProcessor;
import com.ihome.framework.core.event.handler.PayoutDataInDbHandler;
import com.ihome.framework.core.event.handler.PayoutDataReceiveHandler;
import com.ihome.framework.core.event.handler.PayoutDataTallyHandler;
import com.ihome.framework.core.event.handler.PayoutDataToClearHandler;
import com.lmax.disruptor.dsl.ProducerType;

import redis.clients.jedis.Jedis;

/**
 * spring 测试基类
 * 
 * @author 351534
 *
 */
@ContextConfiguration(locations = {"classpath:application.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
public class SpringTestCase implements ApplicationContextAware{

    protected Logger logger = LoggerFactory.getLogger(getClass());


    private static Logger LOGGER = LoggerFactory.getLogger(SpringTestCase.class);

    public static AtomicLong LOG = new AtomicLong(0);

    @Resource
    private CommandBuffer[] buffers;

    @Resource
    private IHomeRedisClient redisClient;


    @Resource
    private PayoutEventBusHandler payoutEventBusHandler;

    @Resource
    private IHomeEventConsumer<PayoutDataEvent> payoutEventBusConsumer;
    @Resource
    private IHomeEventProducer<PayoutDataEvent> payoutEventBusProducer;

    private ApplicationContext applicationContext;
    
    @Resource
    private FrozenAccountLuaCommandScript accountLuaCommandScript;
    
    @Resource
    private TallyAccountLuaCommandScript tallyAccountLuaCommandScript;

    /* (non-Javadoc)
     * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }


    @Test
    public void test1() throws Exception{


        final Long accountMoney = 1000000L;

        int threadSize = 50;

        List<String> accountNoList = new ArrayList<String>();

        final int threadSendSize = 200;

        final int money = 100;

        // 1. 生成账户信息
        for (int i = 0; i < 2; i++) {
            Long accountNo = ThreadLocalRandom.current().nextLong(1, 100000);
            Map<String, String> hash = new HashMap<String, String>();
            hash.put("AccountNo", accountNo.toString());
            hash.put("FrozenMoney", 0L+"");
            hash.put("TotalMoney", accountMoney+"");
            redisClient.hmset("Account-"+accountNo, hash);
            accountNoList.add(accountNo.toString());
        }
        final String[] accountNos = accountNoList.toArray(new String[]{});
        ExecutorService executorService = Executors.newFixedThreadPool(threadSize);

        final CountDownLatch countDownLatch = new CountDownLatch(threadSize*threadSendSize);

        long start = System.currentTimeMillis();
        
        for (int i = 0; i < threadSize; i++) {
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i<threadSendSize; i++) {
                        try{
                            payoutEventBusProducer.publishEvent(new IHomeEventTranslator<PayoutDataEvent>() {
                                @Override
                                public void translateTo(PayoutDataEvent e) {
                                    e.setAccountNo(accountNos[ThreadLocalRandom.current().nextInt(0, accountNos.length)]);
                                    e.setMoney(money);
                                }
                            });
                        }catch (Exception e) {
                            e.printStackTrace();
                        }finally {
                            countDownLatch.countDown();
                        }

                    } 
                }
            });
        }
        countDownLatch.await();
        long end = System.currentTimeMillis();
        Thread.sleep(20000L);

        double time = (end-start) /1000.0;
        double avg =  LOG.get()/time;
        System.out.println("exec -> "+LOG.get() + " time -> "+time + " "+avg +"ops/s" );
        assertTrue(LOG.get() == payoutEventBusHandler.getDisruptor().getCursor()+1);
        for (CommandBuffer buffer : buffers) {
            System.out.println(buffer +" -> " + buffer.get().size() );
            assertTrue(buffer.get().size() == 0);
        }
        int count = 0;
        for (String string : accountNos) {
            System.out.println(redisClient.hmget("Account-"+string, "AccountNo","FrozenMoney","TotalMoney"));

            assertTrue(redisClient.hget("Account-"+string, "FrozenMoney").equals("0"));
            count += (accountMoney - Long.valueOf(redisClient.hget("Account-"+string, "TotalMoney"))) / money;
        }

        assertTrue(count == LOG.get());


    }





}
